using System;
using System.Collections.Generic;
using System.Text;
using Weazel.Math;

namespace Weazel.Gezel.Simulator.Reference.Profiling
{
    class Profiler
    {
        Dictionary<Symid, ProfilingOperation> ops;
        /// <summary>
        /// Stores the .NET type of the signal
        /// </summary>
        Dictionary<Symid, Type> signalTypes;
        
        /// <summary>
        /// Maps from .NET types to the names
        /// which are used to print toggle counts.
        /// </summary>
        static Dictionary<Type, string> signalNames;
        
        Dictionary<Type, uint> evals;
        Dictionary<Type, uint> toggles;
        uint currentevals;
        uint currenttoggles;  // toggles, evals in current clock cycle (generation)
        uint totalevals;
        uint totaltoggles;    // total toggles, evals since cycle 0
        uint lastevals;
        uint lasttoggles;     // total toggles, evals in previous clock cycle
        long generation;

        bool opsMode;
        public bool DisplayOperations
        {
            get { return opsMode; }
            set { opsMode = value; }
        }
        bool cycleMode;
        bool upedgeMode;

        List<uint> weights;
        IList<string> includeset;
        bool makeop_enable;

        static Profiler() {
            signalNames = new Dictionary<Type, string>();
            signalNames[typeof(InputSignal)] = "dpinput";
            signalNames[typeof(OutputSignal)] = "dpoutput";
            signalNames[typeof(IpOutputSignal)] = "ipoutput";
            signalNames[typeof(Signal)] = "(generic signal)";
            signalNames[typeof(FileSignal)] = "(file signal)";
            signalNames[typeof(ProxySignal)] = "(proxy signal)";
            signalNames[typeof(AssignSignal)] = "assign_op";
            signalNames[typeof(RegisterInternalSignal)] = "reg";
            signalNames[typeof(ShrSignal)] = "shr_op";
            signalNames[typeof(ShlSignal)] = "shl_op";
            signalNames[typeof(EqSignal)] = "eq_op";
            signalNames[typeof(NeqSignal)] = "neq_op";
            signalNames[typeof(AddSignal)] = "add_op";
            signalNames[typeof(SubSignal)] = "sub_op";
            signalNames[typeof(MulSignal)] = "mul_op";
            signalNames[typeof(ModSignal)] = "mod_op";
            signalNames[typeof(GteSignal)] = "grteq_op";
            signalNames[typeof(GtSignal)] = "grt_op";
            signalNames[typeof(LteSignal)] = "lteq_op";
            signalNames[typeof(LtSignal)] = "lt_op";
            signalNames[typeof(AndSignal)] = "and_op";
            signalNames[typeof(NotSignal)] = "not_op";
            signalNames[typeof(IorSignal)] = "ior_op";
            signalNames[typeof(XorSignal)] = "xor_op";
            signalNames[typeof(TernarySignal)] = "sel_op";            
        }

        public Profiler(bool upedgeMode)
        {
            this.upedgeMode = upedgeMode;
            this.cycleMode = false;
            this.opsMode = false;
            this.makeop_enable = true;
            ops = new Dictionary<Symid, ProfilingOperation>();
            evals = new Dictionary<Type, uint>();
            toggles = new Dictionary<Type, uint>();
            weights = new List<uint>();
            signalTypes = new Dictionary<Symid, Type>();            
        }

        internal void MakeOperation(AbstractSignal signal, uint width)
        {
            MakeOperation(signal, (int)width);
        }

        internal void MakeOperation(AbstractSignal signal, int width)
        {
            if (signal.Id == Symid.NOSYMBOL) // is an intermediate symbol
                return;
            if (makeop_enable)
            {
                ops[signal.Id] = new ProfilingOperation(upedgeMode, width);
                Type type = signal.GetType();
                signalTypes[signal.Id] = type;
                evals[type] = 0;
                toggles[type] = 0;
            }
        }

        internal void EvaluateOperation(Symid id, Integer newValue)
        {
            if (id == Symid.NOSYMBOL)
                return; // intermediate symbol

            if (ops.ContainsKey(id))
                ops[id].HammingUpdate(newValue, weights);
        }

        internal object ToggleCount(long generation)
        {
            Accumulate(generation);  // update toggle count for current cycle
            return currenttoggles;
        }

        private void Accumulate(long generation)
        {
            if (this.generation != generation)
            {
                // Reset all eval and toggle counts
                foreach (Type type in signalTypes.Values)
                    evals[type] = toggles[type] = 0;
                totalevals = 0;
                totaltoggles = 0;
                //// first collect statistics per operation type
                //map<symid,  rtprofile_op *>::iterator i;
                //for (i = ops.begin(); i != ops.end(); i++) {
                //  evals  [glbSymboltable.getsymbol(i->first)->type()] += i->second->evals;
                //  totalevals   += i->second->evals;
                //  toggles[glbSymboltable.getsymbol(i->first)->type()] += i->second->toggles;
                //  totaltoggles += i->second->toggles;
                //}

                foreach (KeyValuePair<Symid, ProfilingOperation> pair in ops)
                {
                    evals[signalTypes[pair.Key]] += pair.Value.evals;
                    totalevals += pair.Value.evals;
                    toggles[signalTypes[pair.Key]] += pair.Value.toggles;
                    totaltoggles += pair.Value.toggles;

                    //Console.WriteLine("id: {0} evals: {1} toggles: {2}, type: {3}",
                    //    pair.Key, pair.Value.evals, pair.Value.toggles, signalTypes[pair.Key]);
                }

                currentevals = totalevals - lastevals;
                currenttoggles = totaltoggles - lasttoggles;
                lastevals = totalevals;
                lasttoggles = totaltoggles;
                this.generation = generation;
            }
        }

        internal void WriteToggleCounts(System.IO.TextWriter textWriter)
        {
            if (opsMode)
            {
                // Make sure all toggles and evals are calculated
                Accumulate(long.MaxValue);
                textWriter.WriteLine("{0,-30} {1,11} {2,11}", "Type", "Evaluations", "Toggles");
                foreach (Type type in evals.Keys)
                {
                    textWriter.WriteLine("{0,-30} {1,11} {2,11}", signalNames[type], evals[type], toggles[type]);
                }
            }
        }
    }
}
